Skip to content

Improve SiloCast remote UI and reliability - #253

Open
Quick104 wants to merge 1 commit into
mainfrom
t3code/improve-remote-ui-ux
Open

Improve SiloCast remote UI and reliability#253
Quick104 wants to merge 1 commit into
mainfrom
t3code/improve-remote-ui-ux

Conversation

@Quick104

@Quick104 Quick104 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • bring the Android SiloCast remote UI in line with the Apple improvements from feat(ios): center remote controls and map hardware volume silo-apple#199
  • add responsive artwork, transport, scrubber, target, and volume controls, including phone hardware-volume support and optimistic reconciliation
  • retain and synchronize remote volume on Android TV, and canonicalize server identity for more reliable remote connections
  • avoid the foreground-service watchdog crash by letting Media3 promote ongoing sessions after a normal foreground-app service start

Validation

  • ./gradlew test :androidApp:assembleDebug :androidTvApp:assembleDebug
  • focused SiloCastMediaSessionStarterTest regression coverage
  • installed the arm64 debug APK on a Pixel 11 Pro running Android 17
  • launched with existing app data and observed for 50 seconds beyond the previous ~32-second crash window; process remained alive with no new crash-buffer entries

Summary by CodeRabbit

  • New Features

    • Added responsive hardware volume-button controls for remote playback.
    • Improved volume synchronization between the Android app and TV, including mute-state preservation and restoration of the last audible level.
    • Added capability-aware Aspect Ratio and HDR controls to the remote’s top menu.
    • Improved remote layout across screen sizes and font scales, with clearer volume indicators and more consistent control spacing.
  • Bug Fixes

    • Server targets now match reliably across equivalent URL formats, including capitalization and default ports.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1966aa21-9613-4ab6-a2b6-691efdad45ad

📥 Commits

Reviewing files that changed from the base of the PR and between 73d8979 and 4f963d2.

📒 Files selected for processing (15)
  • android-shared/src/androidUnitTest/kotlin/org/siloserver/silo/common/network/AndroidServerIdentityTest.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/MainActivity.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/cast/RemoteVolumeReconciler.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/cast/SiloCastController.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/cast/SiloCastMediaSessionStarter.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/cast/SiloCastRemoteScreen.kt
  • androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/cast/SiloCastTargetPickerSheet.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/cast/RemoteVolumeReconcilerTest.kt
  • androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/cast/SiloCastMediaSessionStarterTest.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/cast/RemotePlaybackIdentityManager.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/cast/SiloCastVolumeTracker.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/cast/TvSiloCastReceiver.kt
  • androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvPlayerScreen.kt
  • androidTvApp/src/androidUnitTest/kotlin/org/siloserver/silo/tv/ui/screens/player/TvSiloCastVolumeStateTest.kt
  • shared/src/androidMain/kotlin/org/siloserver/silo/network/AndroidServerRegistry.kt

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

The change adds canonical server identity matching, serialized cast commands, optimistic volume reconciliation, retained TV volume state, hardware volume control, adaptive remote layouts, and simplified media service startup behavior.

Changes

Cast control and identity

Layer / File(s) Summary
Server identity normalization and validation
shared/src/androidMain/..., android-shared/src/androidUnitTest/..., androidApp/src/androidMain/..., androidTvApp/src/androidMain/...
Server IDs now support exact HTTP(S) round trips and canonical matching for scheme, host, and default ports. Android and TV authorization, launch validation, target filtering, and persistence use normalized identity matching.
Serialized commands and optimistic volume control
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/cast/..., androidApp/src/androidMain/kotlin/org/siloserver/silo/android/MainActivity.kt, androidApp/src/androidUnitTest/...
Cast commands use a transport-bound queue. Volume updates apply optimistic state, reconcile inbound playback frames, and support hardware volume steps. Tests cover burst, reversal, timeout, and clearing behavior.
TV volume retention and playback reporting
androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/cast/..., androidTvApp/src/androidMain/kotlin/org/siloserver/silo/tv/ui/screens/player/TvPlayerScreen.kt, androidTvApp/src/androidUnitTest/...
The TV retains audible volume and mute state across player changes and publishes the resolved state in playback snapshots.
Remote controls and adaptive layout
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/ui/screens/cast/SiloCastRemoteScreen.kt
The remote adds capability-gated aspect-ratio and HDR controls, adaptive now-playing layout behavior, revised volume icons, centered transport controls, and full-width secondary chips.
Media service lifecycle
androidApp/src/androidMain/kotlin/org/siloserver/silo/android/cast/SiloCastMediaSessionStarter.kt, androidApp/src/androidUnitTest/kotlin/org/siloserver/silo/android/cast/SiloCastMediaSessionStarterTest.kt
Media service startup now uses startService. Foreground promotion and the obsolete needsForegroundStart state are removed. Tests validate the simplified action mapping.

Estimated code review effort: 5 (Critical) | ~90 minutes

Merge Risk: ⚪ Minimal · up to 4f963

The PR improves remote controls, volume synchronization, server identity handling, and service reliability; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant MainActivity
  participant SiloCastController
  participant RemoteVolumeReconciler
  participant ControlTransport
  participant TV
  MainActivity->>SiloCastController: stepVolumeOptimistic(step)
  SiloCastController->>RemoteVolumeReconciler: record requested volume
  SiloCastController->>ControlTransport: enqueue volume command
  ControlTransport->>TV: write control frame
  TV-->>SiloCastController: publish playback state
  SiloCastController->>RemoteVolumeReconciler: reconcile inbound volume
Loading

Suggested reviewers: rxwatcher

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 78 functions across 15 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the pull request's main changes: improvements to the SiloCast remote UI and reliability. It is concise and specific enough for project history.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/improve-remote-ui-ux

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f963d2673

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +38 to +41
if (currentVolume != null) {
val clamped = currentVolume.coerceIn(0.0, 1.0)
muted = clamped <= SILENT_VOLUME
if (!muted) retainedVolume = clamped

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve explicit mute state until the player catches up

When handling a mute or unmute command, recordMuted updates muted immediately, but the state provider then calls resolve with the MediaController's current volume. If that asynchronous controller has not reflected the just-issued volume change yet, these lines immediately reverse the explicit state—for example, recordMuted(true, 0.42) followed by resolve(0.42) reports unmuted. The TV consequently sends a contradictory acknowledgement and the remote can flicker or require another interaction; retain the requested mute state until the controller observes the corresponding volume.

Useful? React with 👍 / 👎.

Comment on lines +906 to +908
message.state.copy(
volume = volumeReconciler.reconcile(message.state.volume, now),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reconcile optimistic mute updates alongside volume

When the user toggles mute while an older periodic state frame is already in flight, this copies the inbound isMuted unchanged even though setMuted just updated it optimistically; only volume is reconciled. The stale frame can therefore undo the toggle, and a quick second tap sends the same command again instead of unmuting. Track pending mute requests or otherwise preserve the optimistic isMuted value until its acknowledgement arrives, as is already done for volume.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant